﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using ShevaEngine.Core.Modules.Graphics.Cameras;
using ShevaEngine.Core.Modules.Graphics.Foundation;
using ShevaEngine.Core.Modules.Graphics.Lights;
using ShevaDeferredRenderer.Shadows;

namespace ShevaDeferredRenderer.Lights
{
    /// <summary>
    /// Light Pre Pass Point Light.
    /// This class could be mixed with the directionalLight class or could be coded with the partial class command.
    /// </summary>
    internal class LightPrePass
    {
        /// <summary>Singleton.</summary>
        public static readonly LightPrePass Instance = new LightPrePass();

        /// <summary>The shader effect.</summary>
        private Effect Effect { get; set; }       

        /// <summary>
        /// Load shader
        /// </summary>
        private LightPrePass()
        {
            this.Effect = ShevaEngine.Core.ShevaEngine.Instance.Content.Load<Effect>(@"Effects\LightPrePass\PointLight");            
        }

        /// <summary>
        /// Render to the light pre pass texture.
        /// </summary>
        public void RenderNoneSM(Light light, Camera camera,
            RenderTarget2D depthTexture, RenderTarget2D normalTexture, RenderTarget2D motionVectorSpecularPowerTexture,
            RenderTarget2D lightPrePassMap)
        {
            this.Effect.Parameters["World"].SetValue(Matrix.CreateScale(light.Radius) * Matrix.CreateTranslation(light.World.Translation));
            this.Effect.Parameters["View"].SetValue(camera.ViewMatrix);
            this.Effect.Parameters["ViewI"].SetValue(Matrix.Invert(camera.ViewMatrix));
            this.Effect.Parameters["Projection"].SetValue(camera.ProjectionMatrix);

            this.Effect.Parameters["halfPixel"].SetValue(new Vector2(0.5f / depthTexture.Width, 0.5f / depthTexture.Height));
            this.Effect.Parameters["lightColor"].SetValue(light.DiffuseColor.ToVector3());
            this.Effect.Parameters["lightPosition"].SetValue(Vector3.Transform(light.World.Translation, camera.ViewMatrix));
            this.Effect.Parameters["lightIntensity"].SetValue(light.Intensity);
            this.Effect.Parameters["lightRadius"].SetValue(light.Radius);
            this.Effect.Parameters["depthTexture"].SetValue(depthTexture);
            this.Effect.Parameters["normalTexture"].SetValue(normalTexture);
            this.Effect.Parameters["motionVectorSpecularPowerTexture"].SetValue(motionVectorSpecularPowerTexture);
            this.Effect.Parameters["farPlane"].SetValue(camera.FarPlane);

            this.Effect.Parameters["lightView"].SetValue(Matrix.CreateLookAt(
                light.World.Translation, light.World.Translation - new Vector3(0, 1, 0), Vector3.UnitZ));
            this.Effect.Parameters["Camera"].SetValue(Matrix.Invert(camera.ViewMatrix));

            float cameraToCenter = Vector3.Distance(camera.Position, light.World.Translation) - camera.NearPlane;

            this.Effect.Parameters["insideBoundingLightObject"].SetValue(cameraToCenter < light.Radius);

            if (cameraToCenter < light.Radius)
                ShevaEngine.Core.ShevaEngine.Instance.GraphicsDevice.RasterizerState = RasterizerState.CullClockwise;
            else
                ShevaEngine.Core.ShevaEngine.Instance.GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;

            this.Effect.CurrentTechnique = this.Effect.Techniques["NoneSM"];
            this.Effect.CurrentTechnique.Passes[0].Apply();

            Sphere.Instance.Render();
        }

        /// <summary>
        /// Render to the light pre pass texture.
        /// </summary>
        public void RenderDualParaboloidSM(Light light, DualParaboloidSM shadowMap, Camera camera,
            RenderTarget2D depthTexture, RenderTarget2D normalTexture, RenderTarget2D motionVectorSpecularPowerTexture,
            RenderTarget2D lightPrePassMap)
        {
            this.Effect.Parameters["World"].SetValue(Matrix.CreateScale(light.Radius) * Matrix.CreateTranslation(light.World.Translation));
            this.Effect.Parameters["View"].SetValue(camera.ViewMatrix);
            this.Effect.Parameters["ViewI"].SetValue(Matrix.Invert(camera.ViewMatrix));            
            this.Effect.Parameters["Projection"].SetValue(camera.ProjectionMatrix);

            this.Effect.Parameters["halfPixel"].SetValue(new Vector2(0.5f / depthTexture.Width, 0.5f / depthTexture.Height));
            this.Effect.Parameters["lightColor"].SetValue(light.DiffuseColor.ToVector3());
            this.Effect.Parameters["lightPosition"].SetValue(Vector3.Transform(light.World.Translation, camera.ViewMatrix));
            this.Effect.Parameters["lightIntensity"].SetValue(light.Intensity);
            this.Effect.Parameters["lightRadius"].SetValue(light.Radius);
            this.Effect.Parameters["depthTexture"].SetValue(depthTexture);
            this.Effect.Parameters["normalTexture"].SetValue(normalTexture);
            this.Effect.Parameters["motionVectorSpecularPowerTexture"].SetValue(motionVectorSpecularPowerTexture);
            this.Effect.Parameters["farPlane"].SetValue(camera.FarPlane);

            this.Effect.Parameters["shadowMapTop"].SetValue(shadowMap.ShadowMapTop);
            this.Effect.Parameters["shadowMapBottom"].SetValue(shadowMap.ShadowMapBottom);

            this.Effect.Parameters["lightView"].SetValue(Matrix.CreateLookAt(
                light.World.Translation, light.World.Translation - new Vector3(0, 1, 0), Vector3.UnitZ));                        
            this.Effect.Parameters["Camera"].SetValue(Matrix.Invert(camera.ViewMatrix));            

            float cameraToCenter = Vector3.Distance(camera.Position, light.World.Translation) - camera.NearPlane;            

            this.Effect.Parameters["insideBoundingLightObject"].SetValue(cameraToCenter < light.Radius);

            if (cameraToCenter < light.Radius)
                ShevaEngine.Core.ShevaEngine.Instance.GraphicsDevice.RasterizerState = RasterizerState.CullClockwise;
            else
                ShevaEngine.Core.ShevaEngine.Instance.GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;

            this.Effect.CurrentTechnique = this.Effect.Techniques["DualParaboloidSM"];
            this.Effect.CurrentTechnique.Passes[0].Apply();
            
            Sphere.Instance.Render();
        }
    }
}
